/********************************************************************/
/*          Sio32MultiLoad.c                                        */
/*            32bit Serial Communication Multi-load/Library         */
/*             (Timer Interrupt Send)                               */
/*                                                                  */
/*          Copyright (C) 2001 NINTENDO Co.,Ltd.                    */
/********************************************************************/
//#define CODE32
#include "Sio32MultiLoad.h"


/*-------------------- Global Variable    ----------------------------*/

Sio32MultiLoadArea  S32ml;    // 32bit Serial Communication Multi-load Work Area

static const u8 MultiSioLib_Var[]="Sio32MultiLoad010214";


/*------------------------------------------------------------------*/
/*32bit Serial Communication Multi-load Initialization              */
/*------------------------------------------------------------------*/

void Sio32MultiLoadInit(u32 Type, void *Datap)
{
    s32     CheckSum = 0;
    int     i;

    *(vu16 *)REG_IME = 0;
    *(vu16 *)REG_IE &= ~(SIO_INTR_FLAG                  // Disable SIO & Timer Interrupt
                       | SIO32ML_TIMER_INTR_FLAG);
    *(vu16 *)REG_IME = 1;

    CpuClear(0, &S32ml, sizeof(S32ml), 32);             // Clear 32bit Serial Communication Work Area

#ifdef SIO32ML_DI_FUNC_FAST                             // Copy Function
    CpuCopy(Sio32MultiLoadIntr, IntrFuncBuf, sizeof(IntrFuncBuf), 32);
#endif

    *(vu32 *)REG_SIOCNT = SIO_MULTI_MODE                // Stop Multi-play Communication
                        | MULTI_SIO_BAUD_RATE_NO;

    S32ml.Datap = Datap;                                // Set Data Pointer
    S32ml.DataCounter = -1;

    if (Type) {                                         // Master Setting
        *(vu32 *)REG_SIO32ML_TIMER = 0;                     // Reset Timer 

        S32ml.Type = SIO_SCK_IN;

        for (i=0; i<SIO32ML_BLOCK_SIZE/4; i++)              // Calculate Checksum Send Data
            CheckSum += ((s32 *)Datap)[i];
        S32ml.CheckSum = ~CheckSum;

        *(vu16 *)REG_SIOCNT = SIO_32BIT_MODE;               // Switch SIO (Previous Code)
        *(vu16 *)REG_SIOCNT = SIO_32BIT_MODE
                            | SIO_SCK_IN | SIO_IN_SCK_256K;
    }
}


/*------------------------------------------------------------------*/
/*          32bit Serial Communication Multi-load Main              */
/*------------------------------------------------------------------*/

u32 Sio32MultiLoadMain(u32 *ProgressCounterp)
{
    s32     DataCounter, DataCounterBak;
    int     i, ii;

    switch (S32ml.State) {
        case 0: if (S32ml.Type || S32ml.FrameCounter)       // Slave SIO Mode Switch Delay Wait
                    S32ml.State = 1;
                break;
        case 1: if (S32ml.Type == SIO_SCK_IN) {             // Master Wait
                    if (S32ml.FrameCounter < SIO32ML_MODE_WAIT_FRAMES)
                        break;
                } else                                      // Slave
                    *(vu16 *)REG_SIOCNT = SIO_32BIT_MODE;   // Switch SIO Mode

                *(vu32 *)REG_SIODATA32 = 0;                 // Clear Data Register
                *(vu16 *)REG_IF = SIO_INTR_FLAG             // Reset IF
                                | SIO32ML_TIMER_INTR_FLAG;

                if (S32ml.Type == SIO_SCK_IN){              // Master Setting 
                    *(vu16 *)REG_SIOCNT |= SIO_ENABLE;          // Start Communication

                    *(vu32 *)REG_SIO32ML_TIMER                  // Start Timer
                                     = SIO32ML_TIMER_COUNT
                                     | TMR_PRESCALER_1CK
                                     | TMR_IF_ENABLE | TMR_ENABLE;

                    *(vu16 *)REG_IME = 0;
                    *(vu16 *)REG_IE |= SIO32ML_TIMER_INTR_FLAG; // Enable Timer Interrupt
                    *(vu16 *)REG_IME = 1;
                } else {                                    // Slave Setting
                    *(vu16 *)REG_SIOCNT |= SIO_IF_ENABLE        // Start Communication
                                         | SIO_ENABLE;

                    *(vu16 *)REG_IME = 0;
                    *(vu16 *)REG_IE |= SIO_INTR_FLAG;           // Enable SIO INterrupt
                    *(vu16 *)REG_IME = 1;
                }

                S32ml.FrameCounter = 0;
                S32ml.State = 2;
                break;
        case 2: DataCounter = S32ml.DataCounter;
                DataCounterBak = DataCounter;
                if (DataCounter > SIO32ML_BLOCK_SIZE/4)
                                            DataCounter = SIO32ML_BLOCK_SIZE/4;
                else if (DataCounter < 0)   DataCounter = 0;
                if (ProgressCounterp)
                   *ProgressCounterp = DataCounter;         // Set Progress Counter

                if (S32ml.Type != SIO_SCK_IN) {             // Slave
                    while (S32ml.CheckSumCounter < DataCounter) // Receive Data Checksum
                        S32ml.CheckSumTmp += ((s32 *)S32ml.Datap)[S32ml.CheckSumCounter++];
                    if (DataCounterBak > SIO32ML_BLOCK_SIZE/4)
                        if ((S32ml.CheckSum += S32ml.CheckSumTmp) == -1)
                            S32ml.DownloadSuccessFlag = 1;      // Download Success
                }
                if (DataCounterBak > SIO32ML_BLOCK_SIZE/4   // Communication Complete
                 || S32ml.FrameCounter == SIO32ML_LD_TIMEOUT_FRAMES)
                    S32ml.State = 3;                        // Load Timeout
                break;
        case 3: *(vu16 *)REG_IME = 0;
                *(vu16 *)REG_IE &= ~(SIO_INTR_FLAG          // Disable SIO & Timer Interrupt
                                   | SIO32ML_TIMER_INTR_FLAG);
                *(vu16 *)REG_IME = 1;

                *(vu16 *)REG_SIOCNT = SIO_32BIT_MODE;       // Stop SIO32
                *(vu32 *)REG_SIOCNT = SIO_MULTI_MODE;       // Switch SIO Mode
                *(vu32 *)REG_SIOCNT = SIO_MULTI_MODE
                                    | MULTI_SIO_BAUD_RATE_NO;
                *(vu64 *)REG_SIODATA32 = 0;                 // Clear Data Register

                if (S32ml.Type)                             // Master
                    *(vu32 *)REG_SIO32ML_TIMER = 0;             // Reset Timer

                *(vu16 *)REG_IF  =  SIO_INTR_FLAG           // Reset IF
                                 |  SIO32ML_TIMER_INTR_FLAG;

                if (!S32ml.Type)  return 1;                 // Slave Returns First

                S32ml.FrameCounter = 0;
                S32ml.State = 4;
                break;
        case 4: if (S32ml.FrameCounter >= 3)                // Master Returns After
                    return 1;
    }

    S32ml.FrameCounter++;

    return  0;
}


/*==================================================================*/
/*          32bit Serial Communication Multi-load Interrupt Routine */
/*==================================================================*/

void Sio32MultiLoadIntr(void)
{
    u32     RecvTmp;
    u32    *BufpTmp;


    // Save Receive Data

    RecvTmp = *(u32 *)REG_SIODATA32;

    // Slave Receive Preparation

    if (S32ml.Type != SIO_SCK_IN)
        *(vu16 *)REG_SIOCNT |= SIO_ENABLE;


    if (S32ml.Type == SIO_SCK_IN) {
        *(vu16 *)REG_SIO32ML_TIMER_H = 0;                   // Stop Timer

        // Send Data Processing

        if (S32ml.DataCounter < 0)                          // Set Synchronized Data
                *(vu32 *)REG_SIODATA32 = SIO32ML_SYNC_DATA;
        else if (S32ml.DataCounter < SIO32ML_BLOCK_SIZE/4)  // Set Send Data
                *(vu32 *)REG_SIODATA32 = S32ml.Datap[S32ml.DataCounter];
        else    *(vu32 *)REG_SIODATA32 = S32ml.CheckSum;    // Set Checksum
    } else {

        // Receive Data Processing

        if (S32ml.DataCounter < 0) {                        // Check Synchronized Data
            if (RecvTmp != SIO32ML_SYNC_DATA)
                S32ml.DataCounter--;
        } else if (S32ml.DataCounter < SIO32ML_BLOCK_SIZE/4)// Store Receive Data
                S32ml.Datap[S32ml.DataCounter] = RecvTmp;
        else    S32ml.CheckSum = RecvTmp;                   // Store Checksum
    }


    if (S32ml.DataCounter < SIO32ML_BLOCK_SIZE/4 + 3) {
        S32ml.DataCounter++;

        // Start Master Send

        if (S32ml.Type == SIO_SCK_IN) {
                *(vu16 *)REG_SIOCNT |= SIO_ENABLE;          // Restart Send
                *(vu16 *)REG_SIO32ML_TIMER_H                // Restart Timer
                                 = (TMR_PRESCALER_1CK
                                  | TMR_IF_ENABLE | TMR_ENABLE) >> 16;
        }
    }
}

